#include "Audio.h"
#include <stdio.h>
#include <assert.h>


PaError g_err = paNoError;
PaStreamParameters g_inPars = {0}, g_outPars = {0};
PaStream *g_stream;

//internal functions
bool SetParameters(PaHostApiTypeId API_type, int In, int Out);

bool InitAudio(void)
{
    g_err = Pa_Initialize();
	return (g_err == paNoError);
}
void CloseAudio(void)
{
	if (g_err != paNoError)
	{
	    fprintf( stderr, "Error number: %d\n", g_err );
		fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( g_err ) );
	}
	
	Pa_CloseStream(g_stream);
	Pa_Terminate();
}
void PrintAPIInfo(void)
{
	int numAPIs = Pa_GetHostApiCount();
    if( numAPIs < 0 )
    {
        printf( "ERROR: Pa_GetHostApiCount returned 0x%x\n", numAPIs );
        g_err = numAPIs;
		return;
    }

	printf("APIs count = %d\n", numAPIs);
	for (int i = 0; i < numAPIs; i++)
	{
		printf("\n[API %d]\n", i);
		const PaHostApiInfo *pInfo = Pa_GetHostApiInfo(i);
		printf("ID: %d\n", pInfo->type);
		printf("Name: %s\n", pInfo->name);
		printf("Devices: %d\n", pInfo->deviceCount);
		printf("Default In:  %d\n", pInfo->defaultInputDevice);	
		printf("Default Out: %d\n", pInfo->defaultOutputDevice);
	}
}

bool SetParameters(PaHostApiTypeId API_type, int In, int Out)
{
	PaHostApiIndex API = Pa_HostApiTypeIdToHostApiIndex(API_type);
	if (API == paHostApiNotFound)
	{
		printf("API %d is not supported\n", API_type);
	}
	if (API < 0)
	{
		g_err = API;
		return false;
	}

	if (In < 0)
	{
		g_inPars.device = Pa_GetHostApiInfo(API)->defaultInputDevice;
	}
	else
	{
		g_inPars.device = In;
	}


	if (g_inPars.device == paNoDevice)
	{
		fprintf(stderr,"Error: No default input device.\n");
		return false;
	}

	g_inPars.channelCount = 1;
	g_inPars.sampleFormat = PA_SAMPLE_TYPE;
	g_inPars.suggestedLatency = Pa_GetDeviceInfo(g_inPars.device)->defaultLowInputLatency;
	g_inPars.hostApiSpecificStreamInfo = NULL;

	if (Out < 0)
	{
		g_outPars.device = Pa_GetHostApiInfo(API)->defaultOutputDevice;
	}
	else
	{
		g_outPars.device = Out;
	}

	if (g_outPars.device == paNoDevice)
	{
		fprintf(stderr,"Error: No default output device.\n");
		return false;
	}
	g_outPars.channelCount = 1;
	g_outPars.sampleFormat = PA_SAMPLE_TYPE;
	g_outPars.suggestedLatency = Pa_GetDeviceInfo(g_outPars.device)->defaultLowOutputLatency;
	g_outPars.hostApiSpecificStreamInfo = NULL;

	return true;
}


bool CheckDevice(PaHostApiTypeId API_type, int In, int Out, int SampleRate)
{
	PaError err = -1;
	bool bOk = SetParameters(API_type, In, Out);
	if (bOk)
	{
		printf("Checking Stream \"%s\" -> \"%s\"\n", Pa_GetDeviceInfo(g_inPars.device)->name, Pa_GetDeviceInfo(g_outPars.device)->name);
		err = Pa_IsFormatSupported(&g_inPars, &g_outPars, (double)SampleRate);
	}

	return (err == paFormatIsSupported);
}


bool OpenStream(PaHostApiTypeId API_type, int In, int Out, int SampleRate, PaStreamCallback *callback)
{
	assert(callback != NULL);

	bool bOk = SetParameters(API_type, In, Out);

	if (bOk)
	{
		printf("Opening Stream \"%s\" -> \"%s\"\n", Pa_GetDeviceInfo(g_inPars.device)->name, Pa_GetDeviceInfo(g_outPars.device)->name);
	    g_err = Pa_OpenStream(
                &g_stream,
				&g_inPars,
				&g_outPars,
				SampleRate,
				paFramesPerBufferUnspecified,
				0, /* paClipOff, */  /* we won't output out of range samples so don't bother clipping them */
				callback,
				NULL);

		if (g_err != paNoError)
		{
			return false;
		}

		const PaStreamInfo *pInfo = Pa_GetStreamInfo(g_stream);
		printf("Input  latency: %.1f ms\n", pInfo->inputLatency * 1000);
		printf("Output latency: %.1f ms\n", pInfo->outputLatency * 1000);

		g_err = Pa_StartStream(g_stream);

		return (g_err == paNoError);
	}

	return false;
}

bool ListAudioDevices(PaHostApiTypeId API_type)
{
	PaHostApiIndex API = Pa_HostApiTypeIdToHostApiIndex(API_type);

	if (API == paHostApiNotFound)
	{
		printf("API %d is not supported\n", API_type);
	}
	if (API < 0)
	{
		g_err = API;
		return false;
	}

	const PaHostApiInfo *pInfo = Pa_GetHostApiInfo(API);


	printf("INPUTS:\n");
	for (int API_i = 0; API_i < pInfo->deviceCount; API_i++)
	{
		const PaDeviceIndex i = Pa_HostApiDeviceIndexToDeviceIndex(API, API_i);
		const PaDeviceInfo *pInfo = Pa_GetDeviceInfo(i);
		
		if (pInfo->maxInputChannels > 0)
			printf("    %2d. %s\n", i, pInfo->name);
	}
	printf("OUTPUTS:\n");
	for (int API_i = 0; API_i < pInfo->deviceCount; API_i++)
	{
		const PaDeviceIndex i = Pa_HostApiDeviceIndexToDeviceIndex(API, API_i);
		const PaDeviceInfo *pInfo = Pa_GetDeviceInfo(i);
		
		if (pInfo->maxOutputChannels > 0)
			printf("    %2d. %s\n", i, pInfo->name);
	}
	printf("\n\n");

	return true;
}